import ctypes
import os
import time
import sys

class VCR100UCTester:
    """华视VCR-100UC身份证阅读器测试工具"""
    
    def __init__(self):
        self.ter_dll = None
        self.connected = False
        self.port = None
        
        # 加载DLL
        self.load_dll()
        
    def load_dll(self):
        """加载DLL文件"""
        print("=== 加载DLL ===")
        
        # 所有可能的DLL路径
        possible_paths = [
            "Termb.dll",                                                                # 当前目录
            os.path.join(os.path.dirname(os.path.abspath(__file__)), "Termb.dll"),     # 脚本目录
            os.path.join(os.path.dirname(os.path.abspath(__file__)), "CVR100U", "Termb.dll"), # 子目录
            "C:\\Termb.dll",                                                           # C盘根目录
            os.path.join(os.environ.get('WINDIR', 'C:\\Windows'), "System32", "Termb.dll") # 系统目录
        ]
        
        # 尝试加载DLL
        for path in possible_paths:
            if os.path.exists(path):
                try:
                    print(f"尝试加载: {path}")
                    self.ter_dll = ctypes.windll.LoadLibrary(path)
                    print(f"成功加载DLL: {path}")
                    
                    # 检查相关文件
                    dll_dir = os.path.dirname(path)
                    self.check_related_files(dll_dir)
                    
                    return True
                except Exception as e:
                    print(f"加载 {path} 失败: {str(e)}")
        
        print("错误: 无法加载Termb.dll")
        return False
    
    def check_related_files(self, dll_dir):
        """检查相关文件"""
        print("\n=== 检查相关文件 ===")
        related_files = ["sdtapi.dll", "WltRS.dll", "DLL_File.dll"]
        for file in related_files:
            path = os.path.join(dll_dir, file)
            if os.path.exists(path):
                print(f"找到 {file}: {path}")
            else:
                print(f"警告: 未找到 {file}")
        
        # 检查授权文件
        license_path = "C:\\termb.lic"
        if os.path.exists(license_path):
            print(f"找到授权文件: {license_path}")
        else:
            print(f"警告: 未找到授权文件 {license_path}")
            
            # 尝试从项目目录复制
            src_license = os.path.join(dll_dir, "license.dat")
            if os.path.exists(src_license):
                try:
                    import shutil
                    shutil.copy(src_license, license_path)
                    print(f"已复制授权文件 {src_license} 到 {license_path}")
                except Exception as e:
                    print(f"无法复制授权文件: {str(e)}")
    
    def connect(self):
        """尝试连接设备"""
        if not self.ter_dll:
            print("错误: DLL未加载，无法连接")
            return False
            
        print("\n=== 尝试连接设备 ===")
        
        # 尝试多个端口
        for port in range(1001, 1017):
            try:
                print(f"尝试端口 {port}... ", end="")
                result = self.ter_dll.CVR_InitComm(port)
                if result == 1:
                    self.connected = True
                    self.port = port
                    print("成功!")
                    return True
                else:
                    print(f"失败 (错误码: {result})")
            except Exception as e:
                print(f"异常: {str(e)}")
        
        print("错误: 无法连接到设备，请检查设备连接和驱动")
        return False
    
    def disconnect(self):
        """断开连接"""
        if self.connected and self.ter_dll:
            self.ter_dll.CVR_CloseComm()
            self.connected = False
            print("已断开设备连接")
            return True
        return False
    
    def authenticate_card(self):
        """认证卡片"""
        if not self.connected:
            print("错误: 未连接设备，请先连接")
            return False
            
        print("\n=== 尝试认证卡片 ===")
        print("请将身份证放在读卡区...")
        time.sleep(1)  # 稍微等待用户放卡
        
        # 先尝试标准认证
        try:
            print("尝试标准认证... ", end="")
            auth_result = self.ter_dll.CVR_Authenticate()
            if auth_result == 1:
                print("成功!")
                return True
            else:
                print(f"失败 (错误码: {auth_result})")
        except Exception as e:
            print(f"发生异常: {str(e)}")
        
        # 标准认证失败，尝试无判断认证
        try:
            print("尝试无判断认证... ", end="")
            time.sleep(0.5)  # 增加延时
            auth_result = self.ter_dll.CVR_AuthenticateForNoJudge()
            if auth_result == 1:
                print("成功!")
                return True
            else:
                print(f"失败 (错误码: {auth_result})")
        except Exception as e:
            print(f"发生异常: {str(e)}")
        
        print("认证失败，请确保身份证放置正确")
        return False
    
    def read_card_content(self):
        """读取卡内容"""
        if not self.connected:
            print("错误: 未连接设备，请先连接")
            return False
            
        print("\n=== 尝试读取身份证内容 ===")
        
        # 先尝试CVR_Read_Content方法
        try:
            print("尝试CVR_Read_Content... ", end="")
            read_result = self.ter_dll.CVR_Read_Content(1)
            if read_result == 1:
                print("成功!")
                # 尝试直接从内存获取数据
                self.get_id_card_data_direct()
                return True
            else:
                print(f"失败 (错误码: {read_result})")
        except Exception as e:
            print(f"读取内容异常: {str(e)}")
        
        # 尝试CVR_Read_FPContent方法
        try:
            print("尝试CVR_Read_FPContent... ", end="")
            read_result = self.ter_dll.CVR_Read_FPContent()
            if read_result == 1:
                print("成功!")
                # 尝试直接从内存获取数据
                self.get_id_card_data_direct()
                return True
            else:
                print(f"失败 (错误码: {read_result})")
        except Exception as e:
            print(f"发生异常: {str(e)}")
        
        print("读取内容失败")
        return False
    
    def get_id_card_data_direct(self):
        """直接从内存获取身份证信息"""
        print("\n=== 身份证信息 (直接从内存获取) ===")
        try:
            # 创建缓冲区和长度变量
            buffer_size = 128
            name_buffer = ctypes.create_string_buffer(buffer_size)
            name_len = ctypes.c_int(buffer_size)
            
            sex_buffer = ctypes.create_string_buffer(buffer_size)
            sex_len = ctypes.c_int(buffer_size)
            
            nation_buffer = ctypes.create_string_buffer(buffer_size)
            nation_len = ctypes.c_int(buffer_size)
            
            birthday_buffer = ctypes.create_string_buffer(buffer_size)
            birthday_len = ctypes.c_int(buffer_size)
            
            address_buffer = ctypes.create_string_buffer(buffer_size)
            address_len = ctypes.c_int(buffer_size)
            
            id_buffer = ctypes.create_string_buffer(buffer_size)
            id_len = ctypes.c_int(buffer_size)
            
            issue_buffer = ctypes.create_string_buffer(buffer_size)
            issue_len = ctypes.c_int(buffer_size)
            
            start_buffer = ctypes.create_string_buffer(buffer_size)
            start_len = ctypes.c_int(buffer_size)
            
            end_buffer = ctypes.create_string_buffer(buffer_size)
            end_len = ctypes.c_int(buffer_size)
            
            # 获取各项信息
            info = {}
            
            # 获取姓名
            try:
                if self.ter_dll.GetPeopleName(name_buffer, ctypes.byref(name_len)) == 1:
                    info["姓名"] = name_buffer.value.decode('gbk', errors='ignore')
            except Exception as e:
                print(f"获取姓名失败: {e}")
            
            # 获取性别
            try:
                if self.ter_dll.GetPeopleSex(sex_buffer, ctypes.byref(sex_len)) == 1:
                    info["性别"] = sex_buffer.value.decode('gbk', errors='ignore')
            except Exception as e:
                print(f"获取性别失败: {e}")
            
            # 获取民族
            try:
                if self.ter_dll.GetPeopleNation(nation_buffer, ctypes.byref(nation_len)) == 1:
                    info["民族"] = nation_buffer.value.decode('gbk', errors='ignore')
            except Exception as e:
                print(f"获取民族失败: {e}")
            
            # 获取出生日期
            try:
                if self.ter_dll.GetPeopleBirthday(birthday_buffer, ctypes.byref(birthday_len)) == 1:
                    info["出生日期"] = birthday_buffer.value.decode('gbk', errors='ignore')
            except Exception as e:
                print(f"获取出生日期失败: {e}")
            
            # 获取地址
            try:
                if self.ter_dll.GetPeopleAddress(address_buffer, ctypes.byref(address_len)) == 1:
                    info["地址"] = address_buffer.value.decode('gbk', errors='ignore')
            except Exception as e:
                print(f"获取地址失败: {e}")
            
            # 获取身份证号
            try:
                if self.ter_dll.GetPeopleIDCode(id_buffer, ctypes.byref(id_len)) == 1:
                    info["身份证号"] = id_buffer.value.decode('gbk', errors='ignore')
            except Exception as e:
                print(f"获取身份证号失败: {e}")
            
            # 获取签发机关
            try:
                if self.ter_dll.GetDepartment(issue_buffer, ctypes.byref(issue_len)) == 1:
                    info["签发机关"] = issue_buffer.value.decode('gbk', errors='ignore')
            except Exception as e:
                print(f"获取签发机关失败: {e}")
            
            # 获取有效期起始
            try:
                if self.ter_dll.GetStartDate(start_buffer, ctypes.byref(start_len)) == 1:
                    info["有效期起始"] = start_buffer.value.decode('gbk', errors='ignore')
            except Exception as e:
                print(f"获取有效期起始失败: {e}")
            
            # 获取有效期截止
            try:
                if self.ter_dll.GetEndDate(end_buffer, ctypes.byref(end_len)) == 1:
                    info["有效期截止"] = end_buffer.value.decode('gbk', errors='ignore')
            except Exception as e:
                print(f"获取有效期截止失败: {e}")
            
            # 输出结果
            print("-" * 40)
            if info:
                for key, value in info.items():
                    print(f"{key}: {value}")
            else:
                print("未能获取任何身份证信息")
            print("-" * 40)
            
            return len(info) > 0
            
        except Exception as e:
            print(f"直接获取身份证信息失败: {str(e)}")
            return False
    
    def find_and_display_files(self):
        """查找和显示生成的文件"""
        print("\n=== 查找生成的文件 ===")
        
        # 可能的文件名
        possible_txt_names = ["wz.txt", "baseinfo.txt", "info.txt", "data.txt"]
        possible_img_names = ["zp.bmp", "photo.bmp", "pic.bmp", "zp.jpg", "zp.jpeg"]
        
        # 搜索目录
        search_dirs = [
            os.getcwd(),                                  # 当前目录
            os.path.dirname(os.path.abspath(__file__)),   # 脚本目录
            "C:\\",                                       # C盘根目录
            os.environ.get('TEMP', 'C:\\Temp'),           # 临时目录
            os.path.join(os.environ.get('LOCALAPPDATA', ''), "Temp", "chinaidcard"),  # 官方文档提到的默认路径
        ]
        
        # 查找文本文件
        txt_path = None
        for directory in search_dirs:
            if os.path.exists(directory):
                print(f"搜索目录: {directory}")
                for filename in possible_txt_names:
                    path = os.path.join(directory, filename)
                    if os.path.exists(path):
                        print(f"找到文本文件: {path}")
                        txt_path = path
                        break
                if txt_path:
                    break
        
        # 查找图片文件
        img_path = None
        for directory in search_dirs:
            if os.path.exists(directory):
                for filename in possible_img_names:
                    path = os.path.join(directory, filename)
                    if os.path.exists(path):
                        print(f"找到图片文件: {path}")
                        img_path = path
                        break
                if img_path:
                    break
        
        # 显示文件内容
        if txt_path:
            print("\n=== 身份证信息 (从文件读取) ===")
            try:
                self.display_text_file(txt_path)
            except Exception as e:
                print(f"读取文本文件失败: {str(e)}")
        else:
            print("未找到身份证信息文件")
            
        if img_path:
            print(f"\n图片文件路径: {img_path}")
            print("请使用图片查看器打开查看")
        else:
            print("未找到身份证照片文件")
            
        if not txt_path and not img_path:
            print("警告: 未找到任何生成的文件")
            print("可能原因:")
            print("1. 读卡失败")
            print("2. 程序没有写入权限")
            print("3. 文件生成在其他位置")
            print("\n请尝试以管理员身份运行程序")
    
    def display_text_file(self, file_path):
        """显示文本文件内容"""
        encodings = ['gbk', 'utf-8', 'gb2312']
        
        for encoding in encodings:
            try:
                with open(file_path, 'r', encoding=encoding) as f:
                    content = f.read()
                    print(f"(使用 {encoding} 编码读取)")
                    print("-" * 40)
                    print(content)
                    print("-" * 40)
                    return True
            except UnicodeDecodeError:
                continue
            except Exception as e:
                print(f"读取文件时发生错误: {str(e)}")
                return False
        
        print("无法以支持的编码读取文件")
        return False
    
    def run_test(self):
        """运行完整测试"""
        print("===== 华视VCR-100UC身份证阅读器测试 =====")
        print(f"Python版本: {sys.version}")
        print(f"系统平台: {sys.platform}")
        print(f"当前工作目录: {os.getcwd()}")
        
        if not self.ter_dll:
            print("测试失败: 无法加载DLL")
            return False
            
        try:
            # 连接设备
            if not self.connect():
                return False
                
            # 认证卡片
            if not self.authenticate_card():
                self.disconnect()
                return False
                
            # 读取内容
            if not self.read_card_content():
                self.disconnect()
                return False
                
            # 断开连接
            self.disconnect()
            
            # 查找并显示文件 (仅作为备用)
            print("\n注意: 即使上面已直接显示身份证信息，仍尝试查找文件作为参考")
            self.find_and_display_files()
            
            print("\n===== 测试完成 =====")
            return True
            
        except Exception as e:
            print(f"\n测试过程中发生异常: {str(e)}")
            if self.connected:
                self.disconnect()
            return False

# 直接运行时执行测试
if __name__ == "__main__":
    tester = VCR100UCTester()
    tester.run_test()
    
    print("\n按回车键退出...", end="")
    input() 